#include "Physics.h"

#include "Ball.h"
#include "Bat.h"
#include "Brick.h"
#include "Limiter.h"

CPhysics::CPhysics(void)
{
	
}

CPhysics::~CPhysics(void)
{
}


int CPhysics::Init()
{
	//init ODE
	dInitODE2(0);

	m_world = dWorldCreate();
	m_space = dHashSpaceCreate(0);
	m_joints = dJointGroupCreate(0);


	dWorldSetAutoDisableFlag (m_world,1);

	dWorldSetAutoDisableAverageSamplesCount( m_world, 10 );


	dWorldSetLinearDamping(m_world, 0.00001);
	dWorldSetAngularDamping(m_world, 0.005);
	dWorldSetMaxAngularSpeed(m_world, 200);

	dWorldSetContactMaxCorrectingVel (m_world,0.1);
	dWorldSetContactSurfaceLayer (m_world,0.001);

	dWorldSetGravity(m_world, 0,-1,0);
	dWorldSetCFM (m_world,1e-5);

	//create planes around board


	return 0;
}

int CPhysics::Close()
{
	dJointGroupDestroy(m_joints);
	dSpaceDestroy(m_space);
	dWorldDestroy(m_world);
	dCloseODE();
	return 0;
}

int CPhysics::Step()
{
	if(g_fTime > 0.0)
	{
		dSpaceCollide (m_space,this,&CollisionCallback);
		dWorldQuickStep(m_world, g_fTime);

		dJointGroupEmpty (m_joints);
	}
	return 0;
}

int CPhysics::GetBodyType(dBodyID body)
{
	if(body == NULL)
	{
		return NULL;
	}
	CDrawable* pDrawable = (CDrawable*)dBodyGetData(body);
	return pDrawable->GetType();
}

void CPhysics::CollisionCallback (void *pData, dGeomID geom1, dGeomID geom2)
{
	int i;

	//because static function cannot access non-static members directly, pointer to object
	// is sended by data pointer. It's needed to access world and joints members
	CPhysics* pPhysics = (CPhysics*)pData;

	// exit without doing anything if the two bodies are connected by a joint
	dBodyID b1 = dGeomGetBody(geom1);
	dBodyID b2 = dGeomGetBody(geom2);
	if (b1 && b2 && dAreConnectedExcluding (b1,b2,dJointTypeContact)) return;

	// exit, if two geoms are static
	if ((b1 == NULL) && (b2 == NULL)) return;

	dContact contact[sc_iMaxContacts];   // up to MAX_CONTACTS contacts per box-box

	//check type of two bodies
	if((GetBodyType(b1) == TYPE_BALL) || (GetBodyType(b2) == TYPE_BALL))
	{
		dBodyID bodyBall;
		dBodyID bodySecond;
		//decide which of two bodies is a ball
		if(GetBodyType(b1) == TYPE_BALL)
		{
			bodyBall = b1;
			bodySecond = b2;
		}
		else
		{
			bodyBall = b2;
			bodySecond = b1;
		}
		CBall* pBall = (CBall*)dBodyGetData(bodyBall);

		//if one body is ball and another is bat, bounce is computed in special way
		//check if another body is a Bat.
		if(GetBodyType(bodySecond) == TYPE_BAT)
		{
			CBat* pBat = (CBat*)dBodyGetData(bodySecond);

			pBall->BatBounce(pBat);

		}
		//if second body is a brick, bounce normally, but also destroy the brick (or damage it)
		else if(GetBodyType(bodySecond) == TYPE_BRICK)
		{
			CBrick* pBrick = (CBrick*)dBodyGetData(bodySecond);
			CBall* pBall = (CBall*)dBodyGetData(bodyBall);

			//ball always bounce and have no friction
			for (i=0; i<sc_iMaxContacts; i++) {
				contact[i].surface.mode = dContactBounce | dContactSoftCFM;
				contact[i].surface.mu = 0;
				contact[i].surface.mu2 = 0;
				contact[i].surface.bounce = 1;
				contact[i].surface.bounce_vel = 1;
				contact[i].surface.soft_cfm = 0.0;
			}
			pBrick->Hit(pBall);
		}
		else if(GetBodyType(bodySecond) == TYPE_LIMITER)
		{
			CLimiter* pLimiter = (CLimiter*)dBodyGetData(bodySecond);

			//hit the limiter - player will loose one life
			pLimiter->Hit();
			CBat* pBat = pLimiter->GetAttachedBat();

			//ball out - reset it
			pBall->Out(pBat);
		}
		else
		{
			//ball always bounce and have no friction
			for (i=0; i<sc_iMaxContacts; i++) {
				contact[i].surface.mode = dContactBounce | dContactSoftCFM;
				contact[i].surface.mu = 0;
				contact[i].surface.mu2 = 0;
				contact[i].surface.bounce = 1;
				contact[i].surface.bounce_vel = 1;
				contact[i].surface.soft_cfm = 0.0;
			}
		}
	}
	else
	{
		//other bodies don't bounce
		for (i=0; i<sc_iMaxContacts; i++) {
			contact[i].surface.mode = dContactBounce | dContactSoftCFM;
			contact[i].surface.mu = 100;
			contact[i].surface.bounce = 0;
			contact[i].surface.bounce_vel = 0;
			contact[i].surface.soft_cfm = 0.005;
		}
	}

	
	if (int numc = dCollide (geom1,geom2,sc_iMaxContacts,&contact[0].geom, sizeof(dContact)))
	{
			for (i=0; i<numc; i++) {
				dJointID c = dJointCreateContact (pPhysics->GetWorld(),pPhysics->GetJoints(),contact+i);
				dJointAttach (c,b1,b2);

			}
	}
}
